﻿#include <xy/render/mesh.h>

#define FAST_OBJ_IMPLEMENTATION
#include <fast_obj/fast_obj.h>

namespace xy
{

namespace render
{

using namespace xy::geom;

void mesh::read(std::string path, float scale)
{
    fastObjMesh *objMesh = fast_obj_read(path.c_str());
    if (objMesh == nullptr)
        throw std::runtime_error("Failed to load obj file: " + path);

    auto &theMesh = *this;
    theMesh->materials.resize(objMesh->material_count);
    for (std::size_t i = 0; i < objMesh->material_count; i++)
    {
        auto &mat = theMesh->materials[i];
        mat.name = objMesh->materials[0].name;
        mat.Ka[0] = objMesh->materials[0].Ka[0];
        mat.Ka[1] = objMesh->materials[0].Ka[1];
        mat.Ka[2] = objMesh->materials[0].Ka[2];
        mat.Kd[0] = objMesh->materials[0].Kd[0];
        mat.Kd[1] = objMesh->materials[0].Kd[1];
        mat.Kd[2] = objMesh->materials[0].Kd[2];
        mat.Ks[0] = objMesh->materials[0].Ks[0];
        mat.Ks[1] = objMesh->materials[0].Ks[1];
        mat.Ks[2] = objMesh->materials[0].Ks[2];
        mat.Ke[0] = objMesh->materials[0].Ke[0];
        mat.Ke[1] = objMesh->materials[0].Ke[1];
        mat.Ke[2] = objMesh->materials[0].Ke[2];
        mat.Kt[0] = objMesh->materials[0].Kt[0];
        mat.Kt[1] = objMesh->materials[0].Kt[1];
        mat.Kt[2] = objMesh->materials[0].Kt[2];
        mat.Ns = objMesh->materials[0].Ns;
        mat.Ni = objMesh->materials[0].Ni;
        mat.Tf[0] = objMesh->materials[0].Tf[0];
        mat.Tf[1] = objMesh->materials[0].Tf[1];
        mat.Tf[2] = objMesh->materials[0].Tf[2];
        mat.d = objMesh->materials[0].d;
        mat.illum = objMesh->materials[0].illum;
        mat.map_Ka = objMesh->materials[0].map_Ka.path ? objMesh->materials[0].map_Ka.path : "";
        mat.map_Kd = objMesh->materials[0].map_Kd.path ? objMesh->materials[0].map_Kd.path : "";
        mat.map_Ks = objMesh->materials[0].map_Ks.path ? objMesh->materials[0].map_Ks.path : "";
        mat.map_Ke = objMesh->materials[0].map_Ke.path ? objMesh->materials[0].map_Ke.path : "";
        mat.map_Kt = objMesh->materials[0].map_Kt.path ? objMesh->materials[0].map_Kt.path : "";
        mat.map_Ns = objMesh->materials[0].map_Ns.path ? objMesh->materials[0].map_Ns.path : "";
        mat.map_Ni = objMesh->materials[0].map_Ni.path ? objMesh->materials[0].map_Ni.path : "";
        mat.map_d = objMesh->materials[0].map_d.path ? objMesh->materials[0].map_d.path : "";
        mat.map_bump = objMesh->materials[0].map_bump.path ? objMesh->materials[0].map_bump.path : "";
    }

    // 预先创建顶点，第一个顶点没有用，但是在后面作为点的存在性判断，需要空出来
    theMesh->vertices.reserve(objMesh->position_count - 1);
    for (unsigned int i = 1; i < objMesh->position_count; i++)
        theMesh->vertices.emplace_back(objMesh->positions[3 * i + 0] * scale, objMesh->positions[3 * i + 1] * scale,
                                       objMesh->positions[3 * i + 2] * scale);
    theMesh->normals.resize(theMesh->vertices.size());
    theMesh->texcoords.resize(theMesh->vertices.size());

    // 创建面
    for (unsigned int ii = 0; ii < objMesh->group_count; ii++)
    {
        // 获得组
        const fastObjGroup &grp = objMesh->groups[ii];

        int idx = 0;
        for (unsigned int jj = 0; jj < grp.face_count; jj++)
        {
            unsigned int fv = objMesh->face_vertices[grp.face_offset + jj];

            std::vector<int> fvs;
            for (unsigned int kk = 0; kk < fv; kk++)
            {
                fastObjIndex &mi = objMesh->indices[grp.index_offset + idx];

                // 点坐标，根据 if 条件，mi.p > 0，因此第一个点用不上
                if (mi.p)
                {
                    auto vh = theMesh->vertices[mi.p - 1];
                    fvs.push_back(mi.p - 1);

                    if (mi.t)
                        theMesh->texcoords[mi.p - 1] =
                            vec2f{objMesh->texcoords[2 * mi.t + 0], objMesh->texcoords[2 * mi.t + 1]};

                    if (mi.n)
                        theMesh->normals[mi.p - 1] =
                            vec3f{objMesh->normals[3 * mi.n + 0], objMesh->normals[3 * mi.n + 1],
                                  objMesh->normals[3 * mi.n + 2]};

                    idx++;
                }
            }

            // 创建三角面
            for (auto f : fvs)
                theMesh->indices.push_back(f);
        }
    }

    fast_obj_destroy(objMesh);
}

} // namespace render

} // namespace xy
